﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using BusinessBingo.CommandHandlers.Interfaces;
using BusinessBingo.CommandHandlers.Results;
using BusinessBingo.Commands;
using BusinessBingo.Model;
using BusinessBingo.Queries;
using BusinessBingo.QueryHandlers.Interfaces;
using BusinessBingo.QueryHandlers.Results;
using BusinessBingo.Web.Bindings;
using BusinessBingo.Web.Helpers;
using BusinessBingo.Web.Infrastructure.InfoStores.Interfaces;
using BusinessBingo.Web.Infrastructure.InfoStores.Keys;
using BusinessBingo.Web.Models.JsonResultModels;
using BusinessBingo.Web.Models.ViewModels;

namespace BusinessBingo.Web.Controllers
{
    public class GroupController : Controller
    {
        private ICreateGroupCommandHandler _createGroupCommandHandler;
        private IGetGroupQueryHandler _getGroupQueryHandler;
        private IGetGameQueryHandler _getGameQueryHandler;
        private IGetUserQueryHandler _getUserQueryHandler;
        private IJoinGroupCommandHandler _joinGroupCommandHandler;
        private ICookieRepository _cookieRepository;
        private ICurrentLanguageStore _currentLanguageStore;
        private HttpContextBase _httpContextBase;

        public GroupController(
            ICreateGroupCommandHandler createGroupCommandHandler,
            IGetGroupQueryHandler getGroupQueryHandler,
            IGetGameQueryHandler getGameQueryHandler,
            IGetUserQueryHandler getUserQueryHandler,
            IJoinGroupCommandHandler joinGroupCommandHandler,
            ICookieRepository cookieRepository,
            ICurrentLanguageStore currentLanguageStore,
            HttpContextBase httpContextBase)
        {
            this._createGroupCommandHandler = createGroupCommandHandler;
            this._getGroupQueryHandler = getGroupQueryHandler;
            this._getGameQueryHandler = getGameQueryHandler;
            this._getUserQueryHandler = getUserQueryHandler;
            this._joinGroupCommandHandler = joinGroupCommandHandler;
            this._cookieRepository = cookieRepository;
            this._currentLanguageStore = currentLanguageStore;
            this._httpContextBase = httpContextBase;
        }

        public ActionResult Index(string id)
        {
            GetGroupQuery getGroupQuery = new GetGroupQuery(id);
            GetGroupQueryHandlerResult getGroupQueryHandlerResult = this._getGroupQueryHandler.Execute(getGroupQuery);
            
            GroupIndexViewModel viewModel = new GroupIndexViewModel();
            viewModel.GroupId = getGroupQueryHandlerResult.Group.Id;
            viewModel.GroupName = getGroupQueryHandlerResult.Group.Name;

            return View(viewModel);
        }

        public JsonResult Create(string name)
        {
            LanguageKey language = this._currentLanguageStore.GetPreferredLanguage();

            CreateGroupCommand createGroupCommand = new CreateGroupCommand(name, language);

            this._createGroupCommandHandler.IsMaster = true;
            CreateGroupCommandHandlerResult createGroupCommandHandlerResult =
                this._createGroupCommandHandler.Execute(createGroupCommand);

            if (createGroupCommandHandlerResult.Succeeded == false)
                throw new Exception();

            UrlHelper urlHelper = new UrlHelper(this._httpContextBase.Request.RequestContext);

            string action = urlHelper.Action("Index", "Group");

            GroupCreateJsonResultModel jsonModel = new GroupCreateJsonResultModel();
            jsonModel.Url = string.Join(string.Empty, urlHelper.ApplicationRootPath(), "Group/Index/",
                                        createGroupCommandHandlerResult.Group.Url);

            return Json(jsonModel);
        }

        public RedirectToRouteResult Join(CurrentUserData cookieData, Guid groupId, string userName)
        {
            GetGroupQuery getGroupQuery = new GetGroupQuery(groupId);
            GetGroupQueryHandlerResult getGroupQueryHandlerResult = this._getGroupQueryHandler.Execute(getGroupQuery);

            JoinGroupCommand joinGroupCommand = new JoinGroupCommand(groupId, userName);
            this._joinGroupCommandHandler.IsMaster = true;
            JoinGroupCommandHandlerResult joinGroupCommandHandlerResult =
                this._joinGroupCommandHandler.Execute(joinGroupCommand);

            if(cookieData == default(CurrentUserData))
            {
                this._cookieRepository.AddElement(CookieKeys.UserId, joinGroupCommandHandlerResult.UserId.ToString());
                this._cookieRepository.AddElement(CookieKeys.GameId, joinGroupCommandHandlerResult.GameId.ToString());
            }
            else
            {
                this._cookieRepository.UpdateElement(CookieKeys.UserId, joinGroupCommandHandlerResult.UserId.ToString());
                this._cookieRepository.UpdateElement(CookieKeys.GameId, joinGroupCommandHandlerResult.GameId.ToString());
            }

            return RedirectToAction("Play",
                                    new
                                        {
                                            groupUrl = getGroupQueryHandlerResult.Group.Url,
                                            gameNumber = joinGroupCommandHandlerResult.GameNumber
                                        });
        }

        public ActionResult Play(CurrentUserData cookieData, string groupUrl, int gameNumber)
        {
            GroupPlayViewModel model = new GroupPlayViewModel();
            model.GetOpponentsViewModel = new GetOpponentsViewModel();

            GetGameQuery query = new GetGameQuery(groupUrl, gameNumber);
            GetGameQueryHandlerResult result = this._getGameQueryHandler.Execute(query);

            model.GameId = result.Game.Id;
            model.GroupName = result.GroupName;
            model.IsFinished = result.IsFinished;

            foreach (BingoSet bingoSet in result.BingoSets)
            {
                BingoSetViewModel bingoSetViewModel = ViewModelConverter.GetBingoSetViewModel(bingoSet);
                GetUserQuery getUserQuery = new GetUserQuery(bingoSet.UserId);
                GetUserQueryHandlerResult getUserQueryHandlerResult = this._getUserQueryHandler.Execute(getUserQuery);
                bingoSetViewModel.UserName = getUserQueryHandlerResult.User.Name;

                if (bingoSet.UserId.Equals(cookieData.UserId))
                    model.PlayerBingoSet = bingoSetViewModel;
                else model.GetOpponentsViewModel.BingoSets.Add(bingoSetViewModel);
            }

            return View(model);
        }

        public PartialViewResult GetOpponents(CurrentUserData cookieData, Guid gameId)
        {
            GetOpponentsViewModel model = new GetOpponentsViewModel();
            model.BingoSets = new List<BingoSetViewModel>();

            GetGameQuery query = new GetGameQuery(gameId);
            GetGameQueryHandlerResult result = this._getGameQueryHandler.Execute(query);

            model.IsFinished = result.IsFinished;

            foreach (BingoSet bingoSet in result.BingoSets)
            {
                BingoSetViewModel bingoSetViewModel = ViewModelConverter.GetBingoSetViewModel(bingoSet);
                GetUserQuery getUserQuery = new GetUserQuery(bingoSet.UserId);
                GetUserQueryHandlerResult getUserQueryHandlerResult = this._getUserQueryHandler.Execute(getUserQuery);
                bingoSetViewModel.UserName = getUserQueryHandlerResult.User.Name;

                if (bingoSet.UserId.Equals(cookieData.UserId))
                    continue;
                
                model.BingoSets.Add(bingoSetViewModel);
            }

            return PartialView(model);
        }
    }
}
